﻿using System;
using System.Runtime.Serialization;
using System.Text;
using CI.Interfaces;

namespace CI.Organism
{
    /// <summary>Cell Gene, this performs an operation on a gene</summary>
    [DataContract]
    public class GeneCellular : Gene, IGene
    {
        /// <summary>The Cell which is the what the gene action is tied to</summary>
        [DataMember]
        public ICell Cell { get; set; }

        /// <summary>
        /// Cellular Gene
        /// </summary>
        /// <param name="organism">Organism the gene belongs to</param>
        /// <param name="guid">gene Guid</param>
        public GeneCellular(IOrganism organism, Guid guid) : base(organism, guid) { }

        /// <summary>Carry out the action for the gene</summary>
        public override void Action()
        {
            #region Pre processing checks
            // check and make sure their is a cell to action
            if (Cell == null)
                throw new DNAException(string.Format("Error in DNA, Organism Gene doesnt contain cell to action {0}", Organism.Guid));
            #endregion

            SimEnv.TraceLogger.LogDebug("Actioning Gene of cell type : " + ActionType);
            SimEnv.TraceLogger.LogDebug("Cell type being actioned : " + Cell);

            switch (ActionType)
            {
                case ActionType.AddCell:
                    Cell.AddCell();
                    break;

                case ActionType.DelCell:
                    Cell.DelCell();
                    break;

                case ActionType.Unknown:
                    // if we dont know what to do, lets get out of here
                    break;

                // a reproductive gene will set not applicable
                case ActionType.NotApplicable:
                // remove cell from environment
                case ActionType.RemoveCell:
                    break;

                // if we don't know the action type then bail.
                default:
                    throw new DNAException("Unknown Action Type");
            }
        }

        /// <summary>
        /// Take this parents Gene and create a new Gene to be put in the new child organism
        /// Mutate the current Gene and return a new one, deep class level mutation
        /// </summary>
        /// <param name="mutationRate">mutation rate to be applied</param>
        /// <param name="childOrganism">child Organism it will be going into</param>
        /// <returns>new gene for child</returns>
        public override IGene Mutate(double mutationRate, IOrganism childOrganism)
        {
            // create the new gene
            var childGene = new GeneCellular(childOrganism, Guid)
                                {
                                    // mutate the requirements
                                    Requirements = Requirements.Mutate(mutationRate, childOrganism)
                                };

            // make sure we have at least one cell, this should never happen
            if (childOrganism.Cells == null)
            {
                throw new SystemException("Organism has no cells??");
            }

            // this may be true if this is a new mutated gene cell so we need to create a cell for the gene
            // if it has no neural system then all it can be a is a gene to delete the zygote
            if (Cell == null)
            {
                // if no neural system it can only be a del gene
                if (childOrganism.NeuralSystem == null)
                {
                    // find the zygote and make a new instance of it
                    Cell = childOrganism.Zygote();
                }
                else
                {
                    // find all ICells except IPHYSICAL of different types, the base class Cell and Neuron Type, and create a new one
                    do
                    {
                        // this should only happen when a cell gets created in the next section of code
                        // that we dont want so remove it from the organisms list of cells
                        if (Cell != null)
                        {
                            childOrganism.Cells.Remove(Cell);
                        }

                        // create a new cell
                        Cell = CI.Organism.Cell.CreateNewCell(mutationRate, childOrganism);

                    } while ((Cell is IPhysical) || (Cell.GetType().Equals(typeof(Cell))) || (Cell.GetType().Equals(typeof(Neuron))));

                    // log the new cell                    
                    SimEnv.TraceLogger.LogDebug(string.Format("New Cell created of type {0}, organism total cell count {1}", Cell.GetType(), Organism.Cells.Count));
                }

            }

            // find the cell in the child Organism which matches the one in the current gene
            childGene.Cell = childOrganism.Cells.Find(Cell.Guid);
            // mutate the actual cell, we need to remove the existing one first?
            childOrganism.Cells.Remove(childGene.Cell);
            childGene.Cell = childGene.Cell.Mutate(mutationRate, childOrganism);

            // mutate the action type
            if (Mutation.MutateState(mutationRate))
            {
                childGene.ActionType = ((ActionType)(new Random().Next(1, Enum.GetValues(typeof(ActionType)).Length)));
            }
            else
                childGene.ActionType = ActionType;

            return childGene;
        }

        /// <summary>
        /// do a equals comparison of one gene type to another only comparing the value types, no refference comparisons
        /// </summary>
        /// <param name="gene">gene to compare against</param>
        /// <returns>true if equal value types</returns>
        public override bool EqualsValueTypes(IGene gene)
        {
            // make sure we got a gene to compare against
            if (gene == null)
                return false;

            // compare the mutation rate
            if (Cell.Guid != ((GeneCellular)gene).Cell.Guid)
                return false;

            // check the base class
            return base.EqualsValueTypes(gene);
        }

        /// <summary>
        /// To string
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            // string to return
            var returnString = new StringBuilder();
            // add the base
            returnString.Append(base.ToString() + Environment.NewLine);
            // add the cell type
            returnString.Append("Cell Type : ");
            returnString.Append(Cell.ToString("Extensive"));
            // return string
            return returnString.ToString();
        }
    }
}